#include <napi/native_api.h>
#include <stdio.h>
#include "third_party/global_napi_env.h"
#include <pthread.h>
#include <string.h>

// Bridge function (already implemented in third_party/dartnapi_bridge.c)
int dartnapi_set_env_from_ets(napi_env env);
napi_env dartnapi_get_current_env(void);

typedef enum {
  OP_MATH_RANDOM = 1,
  OP_STRING_VALUE_OF = 2,
} TsfnOp;

typedef struct {
  TsfnOp op;
  int status;
  int done;
  pthread_mutex_t mutex;
  pthread_cond_t cond;
  // inputs
  int32_t int_input;
  char *buf;
  size_t bufsize;
  // outputs
  double double_out;
  size_t out_len;
} TsfnTask;

static napi_threadsafe_function g_tsfn = NULL;

static void TSFN_Callback(napi_env env, napi_value js_cb, void *context, void *data) {
  TsfnTask *task = (TsfnTask *)data;
  task->status = 0;
  do {
    napi_status st;
    napi_value global;
    st = napi_get_global(env, &global);
    if (st != napi_ok) { task->status = -1; break; }

    if (task->op == OP_MATH_RANDOM) {
      napi_value math, random_fn, ret;
      st = napi_get_named_property(env, global, "Math", &math);
      if (st != napi_ok) { task->status = -2; break; }
      st = napi_get_named_property(env, math, "random", &random_fn);
      if (st != napi_ok) { task->status = -3; break; }
      st = napi_call_function(env, math, random_fn, 0, NULL, &ret);
      if (st != napi_ok) { task->status = -4; break; }
      double d = 0.0;
      st = napi_get_value_double(env, ret, &d);
      if (st != napi_ok) { task->status = -5; break; }
      task->double_out = d;
    } else if (task->op == OP_STRING_VALUE_OF) {
      napi_value string_ctor, arg, ret;
      st = napi_get_named_property(env, global, "String", &string_ctor);
      if (st != napi_ok) { task->status = -6; break; }
      st = napi_create_int32(env, task->int_input, &arg);
      if (st != napi_ok) { task->status = -7; break; }
      st = napi_call_function(env, global, string_ctor, 1, &arg, &ret);
      if (st != napi_ok) { task->status = -8; break; }
      size_t len = 0;
      st = napi_get_value_string_utf8(env, ret, NULL, 0, &len);
      if (st != napi_ok) { task->status = -9; break; }
      task->out_len = len;
      if (task->buf && task->bufsize > 0) {
        size_t to_copy = task->bufsize;
        st = napi_get_value_string_utf8(env, ret, task->buf, to_copy, &len);
        if (st != napi_ok) { task->status = -10; break; }
        task->out_len = len;
      }
    } else {
      task->status = -99;
    }
  } while (0);

  pthread_mutex_lock(&task->mutex);
  task->done = 1;
  pthread_cond_signal(&task->cond);
  pthread_mutex_unlock(&task->mutex);
}

static void ensure_tsfn_initialized(napi_env env) {
  if (g_tsfn != NULL) return;
  napi_value name;
  napi_status st = napi_create_string_utf8(env, "DartNapiTsfn", NAPI_AUTO_LENGTH, &name);
  if (st != napi_ok) return;
  napi_value noop;
  st = napi_create_function(env, "noop", NAPI_AUTO_LENGTH, NULL, NULL, &noop);
  if (st != napi_ok) return;
  st = napi_create_threadsafe_function(env, noop, NULL, name, 0, 1, NULL, NULL, NULL, TSFN_Callback, &g_tsfn);
  (void)st;
}

// Blocking submit helper
static int submit_task_blocking(TsfnTask *task) {
  if (g_tsfn == NULL) return -100;
  pthread_mutex_init(&task->mutex, NULL);
  pthread_cond_init(&task->cond, NULL);
  task->done = 0;
  napi_status st = napi_call_threadsafe_function(g_tsfn, task, napi_tsfn_blocking);
  if (st != napi_ok) {
    pthread_cond_destroy(&task->cond);
    pthread_mutex_destroy(&task->mutex);
    return -101;
  }
  pthread_mutex_lock(&task->mutex);
  while (!task->done) {
    pthread_cond_wait(&task->cond, &task->mutex);
  }
  pthread_mutex_unlock(&task->mutex);
  pthread_cond_destroy(&task->cond);
  pthread_mutex_destroy(&task->mutex);
  return task->status;
}

// N-API method: setEnvFromEts()
static napi_value SetEnvFromEts(napi_env env, napi_callback_info info) {
  // Inject current env into the Dart bridge (global storage)
  int rc = dartnapi_set_env_from_ets(env);
  // Keep GlobalNapiEnv in sync for GlobalNapiEnvFFI path
  int rc2 = GlobalEnv_Set(env);
  ensure_tsfn_initialized(env);
  // Basic observable logging to stderr (visible in device logs)
  fprintf(stderr, "[dartnapi] setEnvFromEts called: env=%p rc=%d rc2=%d\n", (void*)env, rc, rc2);
  return NULL;
}

// Exported sync helpers for Dart FFI
FFI_PLUGIN_EXPORT int dartnapi_math_random_sync(double *out_value) {
  if (!out_value) return -1;
  TsfnTask task = {0};
  task.op = OP_MATH_RANDOM;
  int rc = submit_task_blocking(&task);
  if (rc == 0) *out_value = task.double_out;
  return rc;
}

FFI_PLUGIN_EXPORT int dartnapi_string_valueof_sync(int32_t n, char *buf, size_t bufsize, size_t *out_len) {
  TsfnTask task = {0};
  task.op = OP_STRING_VALUE_OF;
  task.int_input = n;
  task.buf = buf;
  task.bufsize = bufsize;
  int rc = submit_task_blocking(&task);
  if (out_len) *out_len = task.out_len;
  return rc;
}

// Module init: export setEnvFromEts
NAPI_MODULE_INIT() {
  napi_value fn = NULL;
  napi_status st = napi_create_function(env, "setEnvFromEts", NAPI_AUTO_LENGTH,
                                        SetEnvFromEts, NULL, &fn);
  if (st == napi_ok) {
    napi_set_named_property(env, exports, "setEnvFromEts", fn);
  }
  return exports;
}


